package com.tambapps.p2p.fandem.util

import com.tambapps.p2p.fandem.FileSharer
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.Locale

object FileUtils {
    /**
     * Display size of bytes with a appropriate unit
     *
     * @param bytes the bytes to display
     * @return a String representing the bytes in the appropriate unit
     */
    fun toFileSize(bytes: Long): String {
        val units = "kMG"
        var denominator: Long = 1
        var i = 0
        while (bytes / (denominator * 1024) > 0 && i < units.length) {
            denominator *= 1024
            i++
        }
        val fileSize = bytes.toFloat() / denominator.toFloat()
        val fileSizeInt = fileSize.toInt()
        return String.format(
            Locale.US, "%s %s",
            if (fileSizeInt.toFloat() == fileSize) fileSizeInt else String.format("%.1f", fileSize),
            if (i == 0) "bytes" else units[i - 1].toString() + "B"
        )
    }

    /**
     * provide a non-existing file in the given directory with the given name if it does not exists.
     * If a file with the given name exits, it will look for a non-existing file with the following name
     * fileName_xxx with xxx a number
     *
     * @param directory the directory in which to get the file
     * @param name      the name wanted of the file
     * @return an non-existing file in the given directory with a name starting with to the given one
     * @throws IOException in case of I/O error
     */
    @Throws(IOException::class)
    fun newAvailableFile(directory: File, name: String): File {
        var file = File(directory, name)
        if (file.exists()) { //searching available file name
            var i = 0
            while (file.exists() && i < 999) {
                val number = StringBuilder(i.toString())
                while (number.length < 3) {
                    number.insert(0, '0')
                }
                val fileName: String = if (name.contains(".")) {
                    val dotIndex = name.indexOf('.')
                    name.substring(0, dotIndex) + '_' + number + name.substring(dotIndex)
                } else {
                    name + '_' + number
                }
                file = File(directory, fileName)
                i++
            }
        }
        if (!file.createNewFile()) {
            throw IOException("Couldn't create new file")
        }
        return file
    }

    fun availableFileInDirectoryProvider(directory: File): FileProvider {
        return FileProvider { name: String -> newAvailableFile(directory, name) }
    }

    @Throws(IOException::class)
    fun newAvailableFile(directory: String, name: String): File {
        return newAvailableFile(File(directory), name)
    }

    fun decodePath(path: String?): String {
        return try {
            URLDecoder.decode(path, StandardCharsets.UTF_8.name())
        } catch (e: UnsupportedEncodingException) {
            throw IllegalArgumentException(e)
        }
    }

    fun bytesToHex(hash: ByteArray): String {
        val hexString = StringBuilder(2 * hash.size)
        for (i in hash.indices) {
            val hex = Integer.toHexString(0xff and hash[i].toInt())
            if (hex.length == 1) {
                hexString.append('0')
            }
            hexString.append(hex)
        }
        return hexString.toString()
    }

    @Throws(IOException::class)
    fun computeChecksum(file: File): String {
        FileInputStream(file).use { inputStream -> return computeChecksum(inputStream) }
    }

    @get:Throws(IOException::class)
    val sha256MessageDigest: MessageDigest
        get() {
            val digest: MessageDigest? = null
            return try {
                MessageDigest.getInstance("SHA-256")
            } catch (e: NoSuchAlgorithmException) {
                throw IOException("Couldn't find MD5 algorithm", e)
            }
        }

    @Throws(IOException::class)
    fun computeChecksum(inputStream: InputStream): String {
        val buffer = ByteArray(FileSharer.DEFAULT_BUFFER_SIZE)
        var count: Int
        val digest = sha256MessageDigest
        while (inputStream.read(buffer).also { count = it } > 0) {
            digest.update(buffer, 0, count)
        }
        val hash = digest.digest()
        return bytesToHex(hash)
    }
}
